Conversation
[migrate] upgrade to PNPM 11, Lint-Staged 17 & other latest Upstream packages [optimize] upgrade GitHub-reward & Lark-GitHub-bot actions
📝 WalkthroughWalkthrough将 ChangesNGO 页面静态化 (SSR → SSG)
估算代码审查工作量🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly Related PRs
Suggested Labels
诗
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
📄 Knowledge review✏️ Documentation updates1 page was updated by changes in this PR.
📝 CI/CD and Deployment Automation — changes@@ -95,6 +95,7 @@
Example fields in the reward task form:
- Task description
+- Task source (URL from an external system, optional)
- Reward currency (dropdown selection)
- Reward amount
- Reward payer (GitHub username, optional) |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
components/Organization/Landscape.tsx (1)
61-72:⚠️ Potential issue | 🟡 Minor | ⚡ Quick win
<ul>元素缺少key属性在
rows.map()中渲染的<ul>元素没有key属性,这会导致 React 在协调时产生警告并可能影响渲染性能。🐛 建议修改
- <ul className={`list-unstyled d-flex flex-${screenNarrow ? 'column' : 'row'} gap-2`}> + <ul key={row.map(([name]) => name).join('-')} className={`list-unstyled d-flex flex-${screenNarrow ? 'column' : 'row'} gap-2`}>或者使用索引作为 key(因为 rows 顺序稳定):
- {rows.map(row => ( + {rows.map((row, index) => ( - <ul className={`list-unstyled d-flex flex-${screenNarrow ? 'column' : 'row'} gap-2`}> + <ul key={index} className={`list-unstyled d-flex flex-${screenNarrow ? 'column' : 'row'} gap-2`}>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@components/Organization/Landscape.tsx` around lines 61 - 72, The `<ul>` element rendered in the `rows.map()` callback is missing a required `key` prop, which causes React reconciliation warnings and impacts rendering performance. Add a `key` prop to the `<ul>` element in the `rows.map()` loop. Since the rows array order is stable, use the index parameter from the map callback as the key value.
🧹 Nitpick comments (5)
pages/NGO/index.tsx (2)
23-34: ⚡ Quick win
observer包装可移除组件内部未使用任何 MobX observable 状态,
observer高阶组件带来的额外订阅开销是不必要的。useContext(I18nContext)返回的t函数是普通 React Context,不需要 MobX 响应式追踪。♻️ 建议修改
-const OrganizationHomePage: FC<InferGetStaticPropsType<typeof getStaticProps>> = observer(props => { +const OrganizationHomePage: FC<InferGetStaticPropsType<typeof getStaticProps>> = props => { const { t } = useContext(I18nContext); return ( <Container className="py-5"> <PageHead title={t('China_NGO_DB')} /> <h1 className="text-center my-4">{t('China_NGO_DB')} 2.0</h1> <ZodiacBar {...props} itemOf={year => ({ title: year, link: `/NGO/${year}` })} /> </Container> ); -}); +};🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@pages/NGO/index.tsx` around lines 23 - 34, The OrganizationHomePage component is unnecessarily wrapped with the observer higher-order component from MobX, which adds subscription overhead even though the component contains no MobX observable state. The only state accessed is from useContext(I18nContext), which is standard React Context and does not require MobX reactive tracking. Remove the observer wrapper from the component declaration so it becomes a regular FC without the MobX observer higher-order component.
12-21: 💤 Low value建议添加
revalidate以支持增量静态再生成(ISR)当前
getStaticProps成功路径未设置revalidate,页面数据仅在构建时生成,后续不会自动更新。如果年份范围数据可能变化,建议添加revalidate以启用 ISR:♻️ 建议修改
- return { props: { startYear, endYear } }; + return { props: { startYear, endYear }, revalidate: 60 * 60 }; // 每小时重新验证🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@pages/NGO/index.tsx` around lines 12 - 21, The getStaticProps function in pages/NGO/index.tsx is missing the revalidate property in its return statement, which prevents Incremental Static Regeneration (ISR) from working. Add a revalidate property to the object returned by getStaticProps to specify how often the page should be regenerated in seconds. This will allow the page data, including the year range retrieved from OrganizationModel.getYearRange(), to be automatically updated at regular intervals instead of only at build time.pages/NGO/[year]/index.tsx (2)
55-57: ⚡ Quick win两个年份页面成功路径均缺少
revalidate,数据将永不自动更新。 当前实现仅在错误路径设置了revalidate: Minute / Second,但成功生成的页面无法通过 ISR 自动刷新,导致数据一旦生成便永久静态化。
pages/NGO/[year]/index.tsx#L55-L57:在return { props: { year, statistic } }后添加revalidate: Minute / Secondpages/NGO/[year]/landscape.tsx#L44-L46:在return { props: JSON.parse(JSON.stringify({ typeMap })) }后添加revalidate: Minute / Second🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@pages/NGO/`[year]/index.tsx around lines 55 - 57, The getStaticProps functions in both affected files are missing the revalidate property in their successful return paths, which prevents ISR automatic updates for the generated pages. In pages/NGO/[year]/index.tsx at lines 55-57, add revalidate: Minute / Second (or appropriate constant value) to the returned object after the props property in the return statement. Similarly, in pages/NGO/[year]/landscape.tsx at lines 44-46, add the same revalidate property to the returned object after returning the typeMap props. This ensures that the successfully generated pages will automatically refresh at the specified interval, matching the revalidate configuration already present in the error paths.
65-88: 💤 Low value
observer包装可移除
OrganizationPage组件未直接观察任何 MobX observable 状态。statistic和year都是从 props 获取的普通对象,移除observer可减少不必要的订阅开销。🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@pages/NGO/`[year]/index.tsx around lines 65 - 88, Remove the observer HOC wrapper from the OrganizationPage component definition. The component does not directly observe any MobX observable state, and both the year and statistic props are plain objects rather than observables, making the observer wrapper unnecessary. Simply unwrap the component function from the observer call in the component assignment.pages/NGO/[year]/landscape.tsx (1)
16-30: ⚖️ Poor tradeoff
getStaticPaths逻辑与[year]/index.tsx重复此处的年份获取逻辑与
pages/NGO/[year]/index.tsx中的getStaticPaths完全相同。建议抽取为共享辅助函数以遵循 DRY 原则:♻️ 建议抽取共享函数
// models/Organization.ts 或单独的 helpers 文件 export async function getNGOYearPaths() { await lark.getAccessToken(); const yearStore = new OrganizationYearStatisticModel(); yearStore.client = lark.client; const years = await yearStore.getAll(); return years .map(({ name }) => name && { params: { year: name! } }) .filter(Boolean) as { params: { year: string } }[]; }然后在两个页面中:
export const getStaticPaths: GetStaticPaths<{ year: string }> = async () => ({ paths: await getNGOYearPaths(), fallback: 'blocking', });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@pages/NGO/`[year]/landscape.tsx around lines 16 - 30, The getStaticPaths function in this file contains the same logic as another getStaticPaths implementation: calling lark.getAccessToken, creating an OrganizationYearStatisticModel instance, and calling getAll to retrieve and transform years. Extract this duplicated logic into a shared helper function (e.g., getNGOYearPaths) that handles the token retrieval, year store initialization, and transformation, returning the formatted paths array. Then refactor the getStaticPaths function in this file to call the helper function instead of duplicating the inline logic. Apply this same refactoring to the other file with identical getStaticPaths logic to eliminate code duplication.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@components/Organization/Landscape.tsx`:
- Around line 61-72: The `<ul>` element rendered in the `rows.map()` callback is
missing a required `key` prop, which causes React reconciliation warnings and
impacts rendering performance. Add a `key` prop to the `<ul>` element in the
`rows.map()` loop. Since the rows array order is stable, use the index parameter
from the map callback as the key value.
---
Nitpick comments:
In `@pages/NGO/`[year]/index.tsx:
- Around line 55-57: The getStaticProps functions in both affected files are
missing the revalidate property in their successful return paths, which prevents
ISR automatic updates for the generated pages. In pages/NGO/[year]/index.tsx at
lines 55-57, add revalidate: Minute / Second (or appropriate constant value) to
the returned object after the props property in the return statement. Similarly,
in pages/NGO/[year]/landscape.tsx at lines 44-46, add the same revalidate
property to the returned object after returning the typeMap props. This ensures
that the successfully generated pages will automatically refresh at the
specified interval, matching the revalidate configuration already present in the
error paths.
- Around line 65-88: Remove the observer HOC wrapper from the OrganizationPage
component definition. The component does not directly observe any MobX
observable state, and both the year and statistic props are plain objects rather
than observables, making the observer wrapper unnecessary. Simply unwrap the
component function from the observer call in the component assignment.
In `@pages/NGO/`[year]/landscape.tsx:
- Around line 16-30: The getStaticPaths function in this file contains the same
logic as another getStaticPaths implementation: calling lark.getAccessToken,
creating an OrganizationYearStatisticModel instance, and calling getAll to
retrieve and transform years. Extract this duplicated logic into a shared helper
function (e.g., getNGOYearPaths) that handles the token retrieval, year store
initialization, and transformation, returning the formatted paths array. Then
refactor the getStaticPaths function in this file to call the helper function
instead of duplicating the inline logic. Apply this same refactoring to the
other file with identical getStaticPaths logic to eliminate code duplication.
In `@pages/NGO/index.tsx`:
- Around line 23-34: The OrganizationHomePage component is unnecessarily wrapped
with the observer higher-order component from MobX, which adds subscription
overhead even though the component contains no MobX observable state. The only
state accessed is from useContext(I18nContext), which is standard React Context
and does not require MobX reactive tracking. Remove the observer wrapper from
the component declaration so it becomes a regular FC without the MobX observer
higher-order component.
- Around line 12-21: The getStaticProps function in pages/NGO/index.tsx is
missing the revalidate property in its return statement, which prevents
Incremental Static Regeneration (ISR) from working. Add a revalidate property to
the object returned by getStaticProps to specify how often the page should be
regenerated in seconds. This will allow the page data, including the year range
retrieved from OrganizationModel.getYearRange(), to be automatically updated at
regular intervals instead of only at build time.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 2148c35b-af68-4be6-a927-68c57330260f
⛔ Files ignored due to path filters (10)
.github/ISSUE_TEMPLATE/reward-task.ymlis excluded by none and included by none.github/scripts/count-reward.tsis excluded by none and included by none.github/scripts/share-reward.tsis excluded by none and included by none.github/scripts/type.tsis excluded by none and included by none.github/workflows/Lark-notification.ymlis excluded by none and included by none.github/workflows/claim-issue-reward.ymlis excluded by none and included by none.github/workflows/statistic-member-reward.ymlis excluded by none and included by nonepackage.jsonis excluded by none and included by nonepnpm-lock.yamlis excluded by!**/pnpm-lock.yaml,!pnpm-lock.yamland included by nonepnpm-workspace.yamlis excluded by none and included by none
📒 Files selected for processing (5)
components/Organization/Landscape.tsxpages/NGO/[year]/index.tsxpages/NGO/[year]/landscape.tsxpages/NGO/index.tsxpages/api/Lark/file/[id]/[name].ts
There was a problem hiding this comment.
Pull request overview
This PR refactors the /NGO route pages to use Static Site Generation (SSG) instead of SSR, while also upgrading dependencies and tightening/modernizing the GitHub reward automation workflows and scripts.
Changes:
- Migrated NGO pages from
getServerSidePropstogetStaticProps/getStaticPathswith Lark-backed data fetching. - Updated reward-related GitHub Actions workflows and Deno scripts (including adding “source” metadata).
- Introduced PNPM workspace config and bumped multiple upstream package versions.
Reviewed changes
Copilot reviewed 14 out of 15 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-workspace.yaml | Adds PNPM workspace + build allowlist config for the repo. |
| pages/NGO/index.tsx | Switches NGO landing page to SSG. |
| pages/NGO/[year]/index.tsx | Switches year detail page to SSG with static paths. |
| pages/NGO/[year]/landscape.tsx | Switches year landscape page to SSG with static paths + ISR-style error fallback. |
| pages/api/Lark/file/[id]/[name].ts | Refactors error-body parsing for Lark file download API. |
| package.json | Upgrades dependencies and changes the test script behavior. |
| components/Organization/Landscape.tsx | Adjusts layout classes to prevent wrapping in group titles. |
| .github/workflows/statistic-member-reward.yml | Pins actions to SHAs and restricts Deno permissions for monthly reward stats. |
| .github/workflows/Lark-notification.yml | Adjusts event serialization to pass GitHub context via env. |
| .github/workflows/claim-issue-reward.yml | Adds concurrency/label gating, pins actions, restricts Deno permissions, and passes new “source” arg. |
| .github/scripts/type.ts | Extends reward data model with optional source. |
| .github/scripts/share-reward.ts | Refactors PR lookup query and reward tagging/pushing; adds source field. |
| .github/scripts/count-reward.ts | Refines reward-tag date filtering and tag pushing behavior. |
| .github/ISSUE_TEMPLATE/reward-task.yml | Adds form guidance and “Task source” input field. |
| await lark.getAccessToken(); | ||
|
|
||
| const organizationStore = new OrganizationModel(); | ||
| organizationStore.client = lark.client; |
| return { | ||
| paths: years.map(({ name }) => name && { params: { year: name! } }).filter(Boolean) as { | ||
| params: { year: string }; | ||
| }[], | ||
| fallback: 'blocking', | ||
| }; |
| return { | ||
| paths: years.map(({ name }) => name && { params: { year: name! } }).filter(Boolean) as { | ||
| params: { year: string }; | ||
| }[], | ||
| fallback: 'blocking', | ||
| }; |
| "build": "next build --webpack", | ||
| "start": "next start", | ||
| "test": "lint-staged && tsc --noEmit" | ||
| "test": "lint-staged && git add . && tsc --noEmit" |
| deno --allow-run --allow-sys --allow-env --allow-read --allow-net=api.github.com \ | ||
| .github/scripts/share-reward.ts \ |
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| run: deno --allow-run --allow-sys --allow-env --allow-read --allow-net=api.github.com .github/scripts/count-reward.ts |
| const allUsers = [author.login, ...assignees.map(({ login }) => login)].uniqueBy(); | ||
|
|
| import 'npm:array-unique-proposal'; | ||
|
|
||
| import { components } from 'npm:@octokit/openapi-types'; | ||
| import { $, argv, YAML } from 'npm:zx'; | ||
|
|
Summary by CodeRabbit
发布说明
性能优化
样式调整
改进